package com.itfreer.gis.data.file;


import org.geotools.data.DefaultTransaction;
import org.geotools.data.FeatureSource;
import org.geotools.data.Transaction;
import org.geotools.data.collection.ListFeatureCollection;
import org.geotools.data.shapefile.ShapefileDataStore;
import org.geotools.data.shapefile.ShapefileDataStoreFactory;
import org.geotools.data.simple.SimpleFeatureCollection;
import org.geotools.data.simple.SimpleFeatureSource;
import org.geotools.data.simple.SimpleFeatureStore;
import org.geotools.feature.FeatureCollection;
import org.geotools.feature.FeatureIterator;
import org.geotools.feature.simple.SimpleFeatureBuilder;
import org.geotools.feature.simple.SimpleFeatureTypeBuilder;
import org.geotools.filter.text.cql2.CQL;
import org.locationtech.jts.geom.Geometry;
import org.opengis.feature.GeometryAttribute;
import org.opengis.feature.Property;
import org.opengis.feature.simple.SimpleFeature;
import org.opengis.feature.simple.SimpleFeatureType;
import org.opengis.filter.Filter;
import org.opengis.referencing.FactoryException;
import org.opengis.referencing.NoSuchAuthorityCodeException;
import org.opengis.referencing.crs.CoordinateReferenceSystem;
import org.springframework.stereotype.Component;

import java.io.File;
import java.io.IOException;
import java.io.Serializable;
import java.nio.charset.Charset;
import java.util.*;

/**
 * shape文件编辑
 */
@Component
public class ShapeFileDataEditServerImp implements ShapeFileDataEditServer {

	/**
	 * 查询
	 *
	 * @param where
	 * @return
	 */
	@Override
	public List<SimpleFeature> queryFeature(String shapeFile, String where) throws Exception {
		// 根据指定目录下的shp下的文件，解析得到Shp属性数据
		ShapefileDataStoreFactory dataStoreFactory = new ShapefileDataStoreFactory();
		List<SimpleFeature> features = new ArrayList<>();
		try {
			ShapefileDataStore sds = (ShapefileDataStore) dataStoreFactory
					.createDataStore(new File(shapeFile).toURI().toURL());
			String typeName = sds.getTypeNames()[0];

			// 如果UTF-8出现乱码则切换GBK
			// sds.setCharset(Charset.forName("UTF-8"));
			sds.setCharset(Charset.forName("GBK"));

			FeatureSource<SimpleFeatureType, SimpleFeature> featureSource = sds.getFeatureSource(typeName);
			FeatureCollection<SimpleFeatureType, SimpleFeature> result = null;
			if (where != null && !where.trim().equals("")) {
				Filter filter = CQL.toFilter(where);
				result = featureSource.getFeatures(filter);
			} else {
				result = featureSource.getFeatures();
			}

			FeatureIterator<SimpleFeature> itertor = result.features();

			while (itertor.hasNext()) {
				SimpleFeature feature = itertor.next();
				features.add(feature);
			}
			itertor.close();
		} catch (Exception e) {
			e.printStackTrace();
		}
		return features;
	}

	/**
	 * 新增
	 *
	 * @param
	 * @return
	 * @throws IOException
	 * @throws FactoryException
	 * @throws NoSuchAuthorityCodeException
	 */
	public Boolean addFeatures(String source, List<SimpleFeature> datas, SimpleFeatureType metadata)
			throws IOException, NoSuchAuthorityCodeException, FactoryException {
		// 根据gis属性数据创建shp文件夹
		// 创建shape文件对象
		File file = new File(source);
		Map<String, Serializable> params = new HashMap<String, Serializable>();
		params.put(ShapefileDataStoreFactory.URLP.key, file.toURI().toURL());
		params.put("create spatial index", Boolean.TRUE);
		ShapefileDataStore ds = (ShapefileDataStore) new ShapefileDataStoreFactory().createNewDataStore(params);

		if (datas == null || datas.size() <= 0) {
			ds.createSchema(metadata);
			ds.setCharset(Charset.forName("GBK"));
			return true;
		} else {
			// 写入文件
			return writeShape(ds, datas);
		}
	}

	private static Boolean writeShape(ShapefileDataStore newDataStore, List<SimpleFeature> features)
			throws IOException, NoSuchAuthorityCodeException, FactoryException {
		GeometryAttribute geom = features.get(0).getDefaultGeometryProperty();
		String geomFieldName = geom.getDescriptor().getLocalName();
		Collection<Property> propertys = features.get(0).getProperties();
		CoordinateReferenceSystem crs = geom.getDescriptor().getCoordinateReferenceSystem();
		SimpleFeatureType ftype = createFeatureType(geom.getValue().getClass(), propertys, crs, geomFieldName);

		// 创建shapefile元数据
		newDataStore.createSchema(ftype);
		newDataStore.setCharset(Charset.forName("GBK"));
		// FeatureWriter<SimpleFeatureType, SimpleFeature> writer =
		// newDataStore.getFeatureWriter(
		// newDataStore.getTypeNames()[0], Transaction.AUTO_COMMIT);

		// 解决数据库空间字段为geom时导出没有空间信息的问题
		// 新建一种SimpleFeature空间属性字段为the_geom
		if (!geomFieldName.equals("the_geom")) {
			List<SimpleFeature> features1 = new ArrayList<>();
			for (SimpleFeature sf : features) {
				SimpleFeatureBuilder sfb = new SimpleFeatureBuilder(ftype);
				SimpleFeature simpleFeature = sfb.buildFeature(null);
				simpleFeature.setAttributes(sf.getAttributes());
				features1.add(simpleFeature);
			}
			features.clear();
			features = features1;
		}

		Transaction transaction = new DefaultTransaction("create");
		String typeName = newDataStore.getTypeNames()[0];
		SimpleFeatureSource featureSource = newDataStore.getFeatureSource(typeName);

		Boolean result = true;
		if (featureSource instanceof SimpleFeatureStore) {
			SimpleFeatureStore featureStore = (SimpleFeatureStore) featureSource;
			SimpleFeatureCollection collection = new ListFeatureCollection(ftype, features);
			featureStore.setTransaction(transaction);
			try {
				featureStore.addFeatures(collection);
				transaction.commit();
			} catch (Exception problem) {
				problem.printStackTrace();
				transaction.rollback();
				result = false;
			} finally {
				transaction.close();
			}
		}
		return result;
	}

	/**
	 * 创建表结构
	 *
	 * @throws FactoryException
	 * @throws NoSuchAuthorityCodeException
	 */
	private static SimpleFeatureType createFeatureType(Class<?> geoTypeClass, Collection<Property> propertys,
			CoordinateReferenceSystem crs, String geomFieldName) throws NoSuchAuthorityCodeException, FactoryException {
		// 定义图形信息和属性信息
		SimpleFeatureTypeBuilder tb = new SimpleFeatureTypeBuilder();
		tb.setCRS(crs);
		tb.setName("shapefile");
		for (Property item : propertys) {
			if (item.getName().toString().equals(geomFieldName)) {
				tb.add("the_geom", geoTypeClass);
			} else {
				tb.add(item.getName().toString(), item.getType().getBinding());
			}
		}
		// build the type
		final SimpleFeatureType ftype = tb.buildFeatureType();
		return ftype;
	}

	/**
	 * 查询
	 *
	 * @param source
	 * @param option
	 *            查询的操作(相交:INTERSECT，包含:CONTAINS等)
	 * @param geometry
	 *            查询空间条件
	 * @return
	 */
	@Override
	public List<SimpleFeature> queryFeature(String source, String option, Geometry geometry) throws Exception {
		return null;
	}

	/**
	 * 编辑
	 *
	 * @return
	 */
	public SimpleFeature updateFeature(String shapeFile, SimpleFeature row, SimpleFeatureType metadata) {
		return null;
	}

	/**
	 * 删除
	 *
	 * @return
	 */
	@Override
	public Boolean deleteFeature(String shapeFile, String where) {
		if (where == null || where.equals("")) {
			return true;
		}
		ShapefileDataStoreFactory dataStoreFactory = new ShapefileDataStoreFactory();
		Boolean result = true;
		try {
			ShapefileDataStore sds = (ShapefileDataStore) dataStoreFactory
					.createDataStore(new File(shapeFile).toURI().toURL());
			sds.setCharset(Charset.forName("GBK"));
			SimpleFeatureSource featureSource = sds.getFeatureSource();

			// 删除数据
			Transaction transaction = new DefaultTransaction("delete");
			if (featureSource instanceof SimpleFeatureStore) {
				SimpleFeatureStore featureStore = (SimpleFeatureStore) featureSource;
				featureStore.setTransaction(transaction);
				try {
					if (where != null && !where.trim().equals("")) {
						Filter filter = CQL.toFilter(where);
						featureStore.removeFeatures(filter);
					}
					transaction.commit();
				} catch (Exception problem) {
					problem.printStackTrace();
					transaction.rollback();
				} finally {
					transaction.close();
				}
			}
		} catch (Exception e) {
			e.printStackTrace();
			result = false;
		}
		return result;
	}

	/**
	 * 获取数据字段
	 *
	 * @param tableName
	 * @return
	 */
	public SimpleFeatureType getTableMetadata(String shapeFile) {
		// 根据指定目录下的shp下的文件，解析得到Shp属性数据
		ShapefileDataStoreFactory dataStoreFactory = new ShapefileDataStoreFactory();
		try {
			ShapefileDataStore sds = (ShapefileDataStore) dataStoreFactory
					.createDataStore(new File(shapeFile).toURI().toURL());
			return sds.getSchema();
		} catch (Exception e) {
			e.printStackTrace();
		}
		return null;
	}
}
